<?php
class BlogUser extends AgaviRbacSecurityUser
{
    /**
     * Load a user details if required.
     *
     */
    public function startup()
    {
        parent::startup();
        $rd = $this->getContext()->getRequest()->getRequestData();
        if (! $this->isAuthenticated() && $rd->hasCookie('login_session_id')) {
            $session_id = $rd->getCookie('login_session_id');
            $session = Doctrine::getTable('Session')->find($session_id);
            $user_address = $_SERVER['REMOTE_ADDR'];
            
            if ($session instanceof Session && $session->user_ip == $user_address) {
                // check that session corresponds to ip address
                $user_id = $session->user_id;
                
                if (is_null($user_id)) {
                    // i.e. this is admin session
                    
                    $this->setAuthenticated(FALSE);
                    $this->clearCredentials();
                    $this->clearAttributes();
                    $this->clearRoles();
                    $this->grantRoles(array('blog-owner'));
                    $this->setAttribute('user_id', NULL); // reserved user id - NULL
                    $this->setAttribute('username', WereWordConfig::get('blog.owner_login'));
                    $this->setAttribute('display_name', WereWordConfig::get('blog.owner_name'));
                    $this->setAttribute('email', WereWordConfig::get('blog.owner_email'));
                    $this->setAttribute('user_type', 'plain');
                    $this->setAuthenticated(TRUE);
                } else {
                    $user = Doctrine::getTable('User')->find($user_id);
                    
                    if ($user instanceof User && $session->user_ip == $user_address) {
                        $this->processUser($user, false);
                    }
                    
                }
                
            } else {
                // corresponding session not found so delete session cookie
                $response = $this->getContext()->getController()->getGlobalResponse();
                $response->setCookie('login_session_id', false);
            }
        }
    }
    
    
    /**
     * Perform login.
     *
     * @param string $username
     * @param string $password
     * @param boolean $isPasswordHashed
     * @throws AgaviSecurityException 
     */
    public function login($username, $password)
    {
        if ($username === WereWordConfig::get('blog.owner_login')) {
            // process blog owner admin separately from the other users
            $orig_hash = WereWordConfig::get('blog.owner_password');
            $salt = substr($orig_hash, 0, 4);
            $hash = self::computeSaltedHash($password, $salt);
            
            if ($orig_hash != $salt.$hash) {
                throw new AgaviSecurityException('password');
            }
            $this->setAuthenticated(FALSE);
            $this->clearCredentials();
            $this->clearAttributes();
            $this->clearRoles();
            $this->grantRoles(array('blog-owner'));
            $this->setAttribute('user_id', NULL); // reserved user id - NULL
            $this->setAttribute('username', $username);
            $this->setAttribute('display_name', WereWordConfig::get('blog.owner_name'));
            $this->setAttribute('email', WereWordConfig::get('blog.owner_email'));
            $this->setAttribute('user_type', 'plain');
            $this->setAuthenticated(TRUE);
            return;
            
        }
        $query = Doctrine_Query::create()
            ->from('User u')
            ->where('u.login_name = ? and u.user_type = ?', array($username, 'plain'))
            ;
        $result = $query->execute();
        $user = $result->getFirst();

        if (!$user instanceof User) {
            throw new AgaviSecurityException('password');
        }
        
        $salt = substr($user->getPasswordHash(), 0, 4);
        $hash = self::computeSaltedHash($password, $salt);
        
        if ($user->getPasswordHash() !== $salt.$hash) {
            throw new AgaviSecurityException('password');
        }
        
        $this->processUser($user);
    }
    /**
     * Perform login using openid URI, create system user if required.
     *
     * @param string $openid_uri
     * @return boolean true if new user was created
     */
    public function openIdLogin($openid_uri)
    {
        $query = Doctrine_Query::create()
            ->from('User u')
            ->where('u.login_name = ? and u.user_type = ?', array($username, 'openid'))
            ;
        $result = $query->execute();
        $user = $result->getFirst();
        $return = false;
        
        if ($user === FALSE) {
            // there is no such user so create it
            $con = $this->getContext()->getDatabaseManager()->getDatabase('blog')->getConnection();
            $con->beginTransaction();
            try {
                /* @var $user User */
                $user = new User();
                $user->login_name = $openid_uri;
                $user->display_name = $openid_uri;
                $user->website = $openid_uri;
                $user->user_type = 'openid';
                $user->password_hash = 'doesnt matter';
                $user_role = new UserRole();
                $user_role->role_name = 'user';
                $user->Roles->add($user_role);
                $user->save($con);
                $con->commit();
            } catch (PropelException $e) {
                $con->rollback();
                throw $e;
            }
            $return = true;
        }
        $this->processUser($user);
        
        return $return;
    }
    
    public function logout(AgaviRequestDataHolder $rd)
    {
        if ($rd->hasCookie('login_session_id')) {
            // destroy session
            $session_id = $rd->getCookie('login_session_id');
            $session = Doctrine::getTable('Session')->find($session_id);
            if ($session instanceof Session) {
                $con = $this->getContext()->getDatabaseManager()->getDatabase('blog')->getConnection();
                $con->beginTransaction();
                try {
                    $session->delete($con);
                    $con->commit();
                } catch (PropelException $e) {
                    $con->rollback();
                    throw $e;
                }
            }
            // delete session cookie                                                 
            $response = $this->getContext()->getController()->getGlobalResponse();
            $response->setCookie('login_session_id', false);
        }
        
        $this->clearCredentials();
        $this->clearAttributes();
        $this->setAuthenticated(false);
    }
    
    /**
     * Reload user attributes, roles etc from corresponding database object
     *
     */
    public function reloadUser()
    {
        if (!$this->isAuthenticated()) {
            return;
        }
        
        $user_id = $this->getAttribute('user_id');
        
        /* @var $users_model Admin_UsersModel */
        $users_model = $this->getContext()->getModel('Users', 'Admin');
        $user = $users_model->getUser($user_id);
        if (is_null($user)) {
            return;
        }
        
        $this->processUser($user, false);
    }
    /**
     * Generate and send session cookie for current user.
     *
     */
    public function rememberSession()
    {
        $session = $this->generateSession();
        $ro = $this->getContext()->getRouting();
        /* @var $response AgaviWebResponse */
        $response = $this->getContext()->getController()->getGlobalResponse();
        $response->setCookie('login_session_id', $session->id, 60*60*24*365, $ro->gen('root'));
    }
    
    /**
     * Generate new Session object
     *                            
     * @return Session            
     */
    protected function generateSession()
    {
        $session = null;
        $i = 1;
        while (++ $i < 10) {
            $session_id = '';
            $j = 0;
            while (++ $j <= 3) {
                $session_id .= mt_rand(10000000, 99999999);
            }
            unset($session);
            $session = Doctrine::getTable('Session')->find($session_id);
            if ($session instanceof Session) {
                // we found session with the same name so continue trying
                $session = null;
                continue;
            } else {
                $con = $this->getContext()->getDatabaseManager()->getDatabase('blog')->getConnection();
                $con->beginTransaction();
                try {
                    /* @var $session Session */
                    $session = new Session();
                    $user_address = $_SERVER['REMOTE_ADDR'];
                    $session->id = $session_id;
                    //var_dump($this->getAttribute('user_id'));
                    $session->user_id = $this->getAttribute('user_id');
                    $session->user_ip = $user_address;
                    $session->save($con);
                    $con->commit();
                } catch (PropelException $e) {
                    $con->rollback();
                    throw $e;
                }
            }
            break;
        }
        return $session;
    }
    
    /**
     * Process instance of User and fill object attributes.
     *
     * @param User $user
     */
    protected function processUser(User $user)
    {
        $this->setAuthenticated(FALSE);
        //$this->setAuthenticated(true);
        $this->clearCredentials();
        $this->clearAttributes();
        $this->clearRoles();
        
        // set user roles
        $roles = array();
        foreach ($user->Roles as $role) {
            $roles[] = $role->role_name;
        }
        $this->grantRoles($roles);
        $this->setAttribute('user_id', $user->id);
        $this->setAttribute('username', $user->login_name);
        $this->setAttribute('display_name', $user->display_name);
        $this->setAttribute('email', $user->email);
        $this->setAttribute('website', $user->website);
        $this->setAttribute('user_type', $user->user_type);
        $this->setAuthenticated(TRUE);
    }
    
    protected function clearRoles()
    {
        unset($this->roles);
        $this->roles = array();
    }
    
    public static function computeSaltedHash($string, $salt)
    {
        return md5($salt . md5($string));
    }
    
    /**
     * Generates new hash for given pasword
     *
     * @param string $password
     */
    public static function genPasswordHash($password)
    {
        $salt = sprintf("%04x", rand(0, 0xffff));
        return $salt.md5($salt.md5($password));
    }
}
?>
